/**
  ******************************************************************************
  * @file    shell_commands.c
  * @author  Iron
  * @date    2021-01-29
  * @version v1.0
  * @brief   shell_commands c file
  */

/** @addtogroup GROUP_SHELL
* @{
*/

/* Private includes ----------------------------------------------------------*/
#include "shell_commands.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/

static struct shell_command_set_t builtin_shell_command_set;

/* Private function prototypes -----------------------------------------------*/

/*---------------------------------------------------------------------------*/
int shell_commands_init(struct shell_command_sets_t *sets)
{
    shell_assert(sets);

    /* Initialize the list. */
    SLIST_INIT(sets);

    /* Initialize the builtin sh commands list. */
    SLIST_INSERT_HEAD(sets, &builtin_shell_command_set, next);

    return 0;
}

/*---------------------------------------------------------------------------*/
int shell_command_set_register(struct shell_command_sets_t *sets, struct shell_command_set_t *set)
{
    struct shell_command_set_t *var = NULL;

    shell_assert(sets);

    if (SLIST_EMPTY(sets))
    {
        SLIST_INSERT_HEAD(sets, set, next);
    }
    else
    {
        SLIST_FOREACH(var, sets, next)
        {
            if (var->next.sle_next == NULL)
            {
                SLIST_INSERT_AFTER(var, set, next);
                break;
            }
        }
    }

    return 0;
}

/*---------------------------------------------------------------------------*/
int shell_command_set_deregister(struct shell_command_sets_t *sets, struct shell_command_set_t *set)
{
    shell_assert(sets);

    SLIST_REMOVE(sets, set, shell_command_set_t, next);

    return 0;
}

/*---------------------------------------------------------------------------*/
/* len = 0 表示全字匹配， len != 0 返回第一个匹配的命令 */
const struct shell_command_t *shell_command_lookup(struct shell_command_sets_t *sets, const char *name, int len)
{
    struct shell_command_set_t *set;
    const struct shell_command_t *cmd;

    shell_assert(sets);

    if(name[0] == '?')
    {
        name = "help";
    }

    SLIST_FOREACH(set, sets, next)
    {
        for (cmd = set->commands; cmd->name != NULL; ++cmd)
        {
            if (len != 0 && !strncmp(cmd->name, name, len))
            {
                return cmd;
            }
            else if (!strcmp(cmd->name, name))
            {
                return cmd;
            }
        }
    }

    return NULL;
}

const char *shell_commands_match(struct shell_command_sets_t *sets, char *buf, int len)
{
    const struct shell_command_t *cmd;

    cmd = shell_command_lookup(sets, buf, len);

    return (cmd == NULL) ? NULL : cmd->name;
}

/*---------------------------------------------------------------------------*/
static char cmd_help(shell_context_t *sh, char *args)
{
    struct shell_command_sets_t *sets = sh->shell_command_sets;
    struct shell_command_set_t *set;
    const struct shell_command_t *cmd;

    shell_assert(sh);
    shell_assert(sh->shell_command_sets);

    sh->shell_printf("Available commands:"  SHELL_NEW_LINE);

    /* Note: we explicitly don't expend any code space to deal with shadowing */
    SLIST_FOREACH(set, sets, next)
    {
        for (cmd = set->commands; cmd->name != NULL; ++cmd)
        {
            sh->shell_printf("%s" SHELL_NEW_LINE, cmd->help);
        }
    }

    return 0;
}

/*---------------------------------------------------------------------------*/
static char cmd_clear(shell_context_t *sh, char *args)
{
    shell_assert(sh);

    sh->shell_printf("\033[2J"); // VT100 终端控制码，清屏
    sh->shell_printf("\033[%d;%dH", 0, 0); // VT100 终端控制码，光标移到 y,x

    return 0;
}

/*---------------------------------------------------------------------------*/
static char cmd_history(shell_context_t *sh, char *args)
{
    struct shell_history_head_t *phead;
    struct shell_history_t *elm;
    int index;

    shell_assert(sh);

    if (sh->shell_history == NULL)
    {
        sh->shell_printf("Shell history not init." SHELL_NEW_LINE);
        return 0;
    }

    sh->shell_printf("Shell history queue:"  SHELL_NEW_LINE);

    phead = &sh->shell_history->head;
    index = 0;

    TAILQ_FOREACH(elm, phead, entries)
    {
        index++;

        sh->shell_printf("%d: ", index);
        sh->shell_puts(elm->commands);
        sh->shell_puts(SHELL_NEW_LINE);
    }

    return 0;
}

/*---------------------------------------------------------------------------*/
__WEAK void shell_reset_cmd(shell_context_t *sh, char *args)
{
    sh->shell_printf("rebooting"  SHELL_NEW_LINE);
}

static char cmd_reboot(shell_context_t *sh, char *args)
{
    shell_assert(sh);

    shell_reset_cmd(sh, args);

    return 0;
}

/*---------------------------------------------------------------------------*/
const struct shell_command_t builtin_shell_commands[] =
{
    { "help",                 cmd_help,                 "'> help'   : Type 'help' or '?' show commands help" },
    { "clear",                cmd_clear,                "'> clear'  : Clear the screen" },
    { "history",              cmd_history,              "'> history': List shell history input." },
    { "reboot",               cmd_reboot,               "'> reboot' : Reboot the board by watchdog_reboot()" },
    { NULL, NULL, NULL },
};

static struct shell_command_set_t builtin_shell_command_set =
{
    .next = NULL,
    .commands = builtin_shell_commands,
};

/**
  * @}
  */

/******************* (C)COPYRIGHT 2021 ***** END OF FILE *********************/
